package edu.berkeley.nlp.util;

import java.io.Serializable;
import java.util.Arrays;



/**
 * Class representing a sparse array. A <code>double[]</code> and <code>int[]</code> arrays
 * store a sparse reprsentation of a double array. Arrays are grown behind the scene when necessary.
 * Setting/getting a count takes time O(log n), where n is the number of non-zero elements. 
 * 
 * @author aria42
 *
 */
public class SparseArray<T> implements Serializable {
	
	private static final long serialVersionUID = 42L;
	
	T[] values = (T[]) new Object[0];
	int[] indices = new int[0];
	int length = 0;
	
	private void grow() {
		int curSize = values.length; 
		int newSize = curSize + 10;
		
		T[] newData = (T[]) new Object[newSize];
		System.arraycopy(values, 0, newData, 0, curSize);
		values = newData;
		int[] newIndices = new int[newSize];
		System.arraycopy(indices, 0, newIndices, 0, curSize);
		for (int i=curSize; i < newIndices.length; ++i) {
			newIndices[i] = Integer.MAX_VALUE;
		}
		indices = newIndices;	
	}
	
	public T get(int index) { 
		int res = Arrays.binarySearch(indices, index);
		if (res >= 0 && res < length) {
			return values[res];
		}
		return null;
	}
	
	public int size() {
		return length;
	}

	public void put(int index0, T x) {
		int res = Arrays.binarySearch(indices, index0);
		// Greater than everything
		if (res >= 0 && res < length) {			
			values[res] = x;
			return;
		}
		if (length+1 >= values.length) {
			grow();
		}
		// In the middle		
		int insertionPoint = -(res+1);
		assert insertionPoint >= 0 && insertionPoint <= length : String.format("length: %d insertion: %d",length,insertionPoint);
		// Shift The Stuff After
		System.arraycopy(values, insertionPoint, values, insertionPoint+1, length-insertionPoint);
		System.arraycopy(indices, insertionPoint, indices, insertionPoint+1, length-insertionPoint);
		indices[insertionPoint] = index0;
		values[insertionPoint] = x;
		length++;
	}
	
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("{ ");
		for (int i=0; i < length; ++i) {
			builder.append(String.format("%d : %.5f",indices[i],values[i]));
			builder.append(" ");
		}
		builder.append(" }");
		return builder.toString();
	}
	
	public String toString(Indexer<?> indexer) {
		StringBuilder builder = new StringBuilder();
		builder.append("{ ");
		for (int i=0; i < length; ++i) {
			builder.append(String.format("%s : %.5f",indexer.getObject(indices[i]),values[i]));
			builder.append(" ");
		}
		builder.append("}");
		return builder.toString();
	}
	
	public static void main(String[] args) {
		SparseArray<Double> sv = new SparseArray<Double>();
		sv.put(0, 1.0);
		sv.put(1, 2.0);
		sv.put(4, -2.0);
		System.out.println(sv);
	}

	
}
